// 定义蛇
class Snake {
  // 表示蛇头的元素
  head: HTMLElement
  // 蛇的身体（包括蛇头）
  bodies: HTMLCollection
  // 获取蛇的容器
  element: HTMLElement

  constructor() {
    this.element = document.getElementById('snake')!
    // querySelector 返回的是别的类型
    // 做个类型断言
    this.head = document.querySelector('#snake > div') as HTMLElement
    // querySelectorAll 获取的是死的标签，无法改变
    this.bodies = this.element.getElementsByTagName('div')
  }

  // 获取蛇头的 X 坐标
  get X() {
    return this.head.offsetLeft
  }

  // 获取蛇头的 Y 坐标
  get Y() {
    return this.head.offsetTop
  }

  // 修正 X 的值 - 防止掉头
  amendX(X: number): number {   
    // 此时在修改 X 值，即蛇在左右移动
    // 不允许掉头
    if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === X) {
      // 如果要发生掉头，应该让蛇向反方向继续移动
      // 或者可以限制按键吧
      if (X > this.X) {
        // 新值 value 大于旧值 X，说明蛇在向右走
        // 此时应该让蛇继续向左走
        X = this.X - 10
      } else {
        X = this.X + 10
      }
    }
    return X
  }
  // 修正 Y 的值 - 防止掉头
  amendY(Y: number): number {   
    if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === Y) {
      // 如果发生了掉头，应该让蛇向反方向继续移动 —— 不行，会出现将要吃到食物时掉头，食物会存在一会的bug
      // 或者可以限制按键吧
      if (Y > this.Y) {
        // 新值 value 大于旧值 X，说明蛇在向下走
        // 此时应该让蛇继续向上走
        Y = this.Y - 10
      } else {
        Y = this.Y + 10
      }
    }
    return Y
  }

  // 设置蛇头的 X 坐标
  set X(value: number) { 
    // X 的值的合法范围 [0, 290]
    if (value < 0 || value > 290) {
      // 蛇撞墙了，就抛出一个异常
      throw new Error('蛇撞墙了！')
    }    
    // 如果新值和旧值相同，则直接返回，不再修改
    if (this.X === value) {
      return
    }
    // 移动身体
    this.moveBody()
    this.head.style.left = value + 'px'
    // 检查有没有撞到自己
    this.checkHeadBody()
  }

  // 设置蛇头的 Y 坐标
  set Y(value: number) { 
    // Y 的值的合法范围 [0, 290]
    if (value < 0 || value > 290) {
      // 蛇撞墙了
      throw new Error('蛇撞墙了！')
    }
    if (this.Y === value) {
      return
    }
    this.moveBody()
    this.head.style.top = value + 'px'
    // 检查有没有撞到自己
    this.checkHeadBody()
  }

  // 蛇增加身体的方法
  addBody() {
    // 向 element 里面添加 div
    this.element.insertAdjacentHTML("beforeend", "<div></div>")
  }

  // 添加一个蛇身体移动的方法
  moveBody() {
    // 将后面的身体设置为前面身体的位置
    // 第 2 节 = 第 1 节的位置
    // 蛇头位置不需要修改
    // 遍历获取所有的身体
    for (let i = this.bodies.length - 1; i > 0; i--) {
      // 获取前面身体的位置
      // Element 是 HTMLElement 的接口
      let X = (this.bodies[i - 1] as HTMLElement).offsetLeft;
      let Y = (this.bodies[i - 1] as HTMLElement).offsetTop;
      // 将值设置到当前身体上
      // 以 ( 开头，前面的需要加 ;
      (this.bodies[i] as HTMLElement).style.left = X + 'px';
      (this.bodies[i] as HTMLElement).style.top = Y + 'px'
    }
  }

  // 检查蛇头是否撞到身体的方法
  checkHeadBody() {
    // 获取所有身体，检查是否和蛇头的坐标发生重叠
    for (let i = 1; i < this.bodies.length; i++) {
      let bd = this.bodies[i] as HTMLElement
      if (this.X === bd.offsetLeft && this.Y === bd.offsetTop) {
        // 蛇头撞到了身体
        throw new Error('从自己身体上压过去了')
      }
    }
  }
}

export default Snake
